<script setup lang="ts">
import {nextTick, ref} from "vue";
import type TooltipPosition from "@/components/tooltip/TooltipPosition.ts";

defineProps({
  msg: {
    type: String,
    default: ''     // 用空串而不是 null
  }
})

const tooltipRef = ref<HTMLElement | null>(null)
//气泡框显示与否
const tooltipVisible = ref(false)
//气泡框控件
const tooltipContent = ref<HTMLElement | null>(null)
//气泡框的css样式
const tooltipStyle = ref<Record<string, string>>({})

const tooltipPlacement = ref<'top' | 'bottom' | 'left' | 'right'>('top')
//气泡框箭头所在位置
const arrowLeft = ref("")

let hideTimeout: number | null = null

const showTooltip = async () => {
  const el = tooltipRef.value
  if (!el) return
  //检查slot插槽内容是否溢出
  const isOverflow = el.scrollWidth > el.clientWidth
  if (!isOverflow) {
    tooltipVisible.value = false
    return
  }
  tooltipVisible.value = true
  //等待 tooltip-content 渲染到 DOM
  await nextTick()

  const tooltipEl = tooltipContent.value
  if (!tooltipEl) return

  // 等待样式应用完成，确保能获取到正确的尺寸
  await waitForElementSize(tooltipEl)

  const {top, left, placement, arrowLeft: arrowPx} = computeTooltipPosition(el, tooltipEl)
  tooltipPlacement.value = placement
  arrowLeft.value = `${arrowPx}px`

  tooltipStyle.value = {
    position: 'absolute',
    top: `${top}px`,
    left: `${left}px`,
    opacity: '1', // 位置计算完成后显示
  }
}


const delayedHide = () => {
  hideTimeout = window.setTimeout(() => {
    tooltipVisible.value = false
  }, 100)
}

const cancelHide = () => {
  if (hideTimeout !== null) {
    clearTimeout(hideTimeout)
    hideTimeout = null
  }
  showTooltip()
}

/**
 * 等待元素获得正确的尺寸
 */
const waitForElementSize = (element: HTMLElement): Promise<void> => {
  return new Promise((resolve) => {
    const checkSize = () => {
      const rect = element.getBoundingClientRect()
      // 检查元素是否有实际的尺寸
      if (rect.width > 0 && rect.height > 0 && element.offsetWidth > 0 && element.offsetHeight > 0) {
        resolve()
      } else {
        // 如果尺寸还是0，继续等待
        requestAnimationFrame(checkSize)
      }
    }
    checkSize()
  })
}

/**
 * 计算 tooltip 最佳展示位置及定位信息
 */
const computeTooltipPosition = (targetEl: HTMLElement, tooltipEl: HTMLElement): TooltipPosition => {
  const rect = targetEl.getBoundingClientRect()
  const scrollX = window.scrollX
  const scrollY = window.scrollY

  //当前元素的顶部距离视口顶部的距离
  const spaceAbove = rect.top
  //当前元素的底部距离视口底部的距离
  const spaceBelow = window.innerHeight - rect.bottom
  //当前元素的左侧距离视口左侧的距离
  const spaceLeft = rect.left
  //当前元素的右侧距离视口右侧的距离
  const spaceRight = window.innerWidth - rect.right

  //气泡框宽高 - 使用getBoundingClientRect()获取更准确的尺寸
  const tooltipRect = tooltipEl.getBoundingClientRect()
  const tooltipWidth = tooltipRect.width || tooltipEl.offsetWidth
  const tooltipHeight = tooltipRect.height || tooltipEl.offsetHeight

  // 如果仍然获取不到正确尺寸，使用默认值
  const finalTooltipWidth = tooltipWidth || 200
  const finalTooltipHeight = tooltipHeight || 32

  //默认优先顺序：top > bottom > right > left
  let placement: 'top' | 'bottom' | 'left' | 'right' = 'top'
  if (spaceAbove > finalTooltipHeight + 10) {
    placement = 'top'
  } else if (spaceBelow > finalTooltipHeight + 10) {
    placement = 'bottom'
  } else if (spaceRight > finalTooltipWidth + 10) {
    placement = 'right'
  } else if (spaceLeft > finalTooltipWidth + 10) {
    placement = 'left'
  }

  let top = 0
  let left = 0
  let arrowLeft = 0

  const centerX = rect.left + scrollX + rect.width / 2
  const centerY = rect.top + scrollY + rect.height / 2

  switch (placement) {
    case 'top':
      top = rect.top + scrollY - finalTooltipHeight - 10
      left = centerX - finalTooltipWidth / 2
      arrowLeft = finalTooltipWidth / 2
      break
    case 'bottom':
      top = rect.bottom + scrollY + 10
      left = centerX - finalTooltipWidth / 2
      arrowLeft = finalTooltipWidth / 2
      break
    case 'right':
      top = centerY - finalTooltipHeight / 2
      left = rect.right + scrollX + 10
      arrowLeft = 10 // 箭头靠左显示
      break
    case 'left':
      top = centerY - finalTooltipHeight / 2
      left = rect.left + scrollX - finalTooltipWidth - 10
      arrowLeft = finalTooltipWidth - 10 // 箭头靠右显示
      break
  }

  return {top, left, placement, arrowLeft}
}
</script>

<template>
  <span class="tooltip" ref="tooltipRef" @mouseenter="cancelHide" @mouseleave="delayedHide">
    <slot></slot>
  </span>

  <teleport to="body">
    <div v-if="tooltipVisible" ref="tooltipContent" class="tooltip-content" :style="tooltipStyle"
         :class="`placement-${tooltipPlacement}`"
         @mouseenter="cancelHide" @mouseleave="delayedHide">
      {{ msg }}
      <div class="tooltip-arrow"></div>
    </div>
  </teleport>
</template>

<style scoped>
.tooltip {
  width: 100%;
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.tooltip-content {
  pointer-events: auto;
  padding: 6px 10px;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  background: #333;
  color: #fff;
  font-size: 12px;
  z-index: 99999;
  /* 关键：限制最大宽度并启用自动换行 */
  max-width: 500px;
  width: max-content;
  white-space: normal;
  /* 如果内容太长，允许换行,在任意字符处换行 */
  word-break: break-all;
  /* 初始时设置为不可见，直到位置计算完成 */
  opacity: 0;
  transition: opacity 0.1s ease-in-out;
  /* 确保元素不会因为定位问题占据整个宽度 */
  position: absolute;
  top: -9999px;
  left: -9999px;
}

/* 当位置计算完成后显示 */
.tooltip-content[style*="opacity: 1"] {
  opacity: 1;
}

.tooltip-arrow {
  position: absolute;
  width: 10px;
  height: 10px;
  background: #333;
  z-index: -1;
  transform: rotate(45deg);
}

/* top: 箭头在下方 */
.placement-top .tooltip-arrow {
  bottom: -5px;
  left: v-bind(arrowLeft);
  transform: translateX(-50%) rotate(45deg);
}

/* bottom: 箭头在上方 */
.placement-bottom .tooltip-arrow {
  top: -5px;
  left: v-bind(arrowLeft);
  transform: translateX(-50%) rotate(45deg);
}

/* right: 箭头在左边中间 */
.placement-right .tooltip-arrow {
  left: -5px;
  top: 50%;
  transform: translateY(-50%) rotate(45deg);
}

/* left: 箭头在右边中间 */
.placement-left .tooltip-arrow {
  right: -5px;
  top: 50%;
  transform: translateY(-50%) rotate(45deg);
}
</style>